#include "epoll_socket.h"

#include <fcntl.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <string.h>
#include <errno.h>  
#include <iostream> 

#define MAXBUFFSIZE	1024
#define MAXEVENTS	500
#define FDSIZE		1000

epoll_socket::epoll_socket() 
	: running(true)
	, print_flag(false)
	, isblock_client(false)
{
	socketfd = 0;
	memset(&servaddr, 0, sizeof(servaddr));
}

epoll_socket::~epoll_socket()
{
	running = false;
}

bool epoll_socket::open(int port, bool isblock_listen)
{
	if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
		return false;
	}
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
	servaddr.sin_port = htons(port);
	if (!isblock_listen) {
		int flags = fcntl(socketfd, F_GETFL, 0);
		fcntl(socketfd, F_SETFL, flags | O_NONBLOCK);//设置为非阻塞
	}

	//设置重用地址，防止Address already in use
	int on = 1;
	if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "set reuse addr error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}

	//将本地地址绑定到所创建的套接字上  
	if (bind(socketfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "bind socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	//开始监听是否有客户端连接  
	if (listen(socketfd, 5) == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	std::cout << "create socket success\n";
	return true;
}

int epoll_socket::get_socketfd()
{
	return socketfd;
}

int epoll_socket::run()
{
	//创建一个描述符
	if ((epollfd = epoll_create(FDSIZE)) == -1){
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return -1;
	}
	//添加监听描述符事件
	add_event(socketfd, EPOLLIN);
	struct epoll_event events[MAXEVENTS];
	int ret;
	while (running) {
		//获取已经准备好的描述符事件
		ret = epoll_wait(epollfd, events, MAXEVENTS, 1);
		handle_events(events, ret);
	}
	close(epollfd);
	return 0;
}

void epoll_socket::send(std::string item,int fd/*=-1*/)
{
	if(fd<0){
		fds_mutex.Lock();
		std::set<int> fds_ = fds_client;
		fds_mutex.Unlock();
		std::set<int>::iterator it = fds_.begin();
		for (; it!=fds_.end(); ++it)
		{
			buffer_write.add(ItemCache(item,fd));
			mod_event(*it,EPOLLOUT);
		}
	}else{
		fds_mutex.Lock();
		bool f_ = (fds_client.find(fd)!=fds_client.end());
		fds_mutex.Unlock();
		if(f_)
		{
			buffer_write.add(ItemCache(item,fd));
			mod_event(fd,EPOLLOUT);
		}
	}
}

bool epoll_socket::get(std::string &item,int &fd)
{
	bool ret =false;
	ItemCache it;
	if( buffer_read.pop(it))
	{
		item = it.data;
		fd = it.fd;
		ret = true;
	}
	return ret;
}

void epoll_socket::add_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	/*
	//如果是ET模式，设置EPOLLET
	ev.events |= EPOLLET;
	*/
	//设置是否阻塞
	if(!isblock_client){
		int flags = fcntl(fd, F_GETFL);
		fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	}
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}

void epoll_socket::del_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
}

void epoll_socket::mod_event(int fd, int state)
{
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
}

void epoll_socket::add_client(int fd)
{
	fds_mutex.Lock();
	fds_client.insert(fd);
	fds_mutex.Unlock();
}
void epoll_socket::del_client(int fd)
{
	fds_mutex.Lock();
	fds_client.erase(fd);
	fds_mutex.Unlock();
}

void epoll_socket::handle_events(epoll_event * events, int num)
{
	int i;
	int fd;
	//进行选好遍历
	for (i = 0; i < num; i++) {
		fd = events[i].data.fd;
		//根据描述符的类型和事件类型进行处理
		if ((fd == socketfd) && (events[i].events& EPOLLIN)){
			handle_accept();
		}else if (events[i].events & EPOLLIN){
			do_read(fd);
		}else if (events[i].events & EPOLLOUT){
			do_write(fd);
		}else{
			del_client(fd);
			close(fd);
		}
	}
}

bool epoll_socket::handle_accept()
{
	int clifd;
	struct sockaddr_in cliaddr;
	socklen_t cliaddrlen = sizeof(cliaddr);
	clifd = accept(socketfd, (struct sockaddr*)&cliaddr, &cliaddrlen);
	if (clifd == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "listen socket error: %s(errno: %d)\n"
			, strerror(errno), errno);
		return false;
	}
	else {
		char msg[128] = { 0 };
		//获取端口错误
		sprintf(msg,"accept a new client(%d):%s:%d\n"
			, clifd, inet_ntoa(cliaddr.sin_addr)
			, cliaddr.sin_port);
		std::cout << msg;
		//添加一个客户描述符和事件
		add_event(clifd, EPOLLIN);
		add_client(clifd);
	}
}

bool epoll_socket::do_read(int fd)
{
	char buf[MAXBUFFSIZE]={0};
	int buflen = read(fd, buf, MAXBUFFSIZE);
	if (buflen == -1) {
		snprintf(err_msg, sizeof(err_msg)
			, "read error(%d): %s(errno: %d)\n"
			, fd, strerror(errno), errno);
		std::cout << err_msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLIN);
		return false;
	}
	else if (buflen == 0) {
		char msg[128] = { 0 };
		sprintf(msg,"client(%d) close.\n", fd);
		std::cout << msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLIN);
		return true;
	}
	else {
		if(print_flag){
			char msg[MAXBUFFSIZE] = { 0 };
			sprintf(msg, "read message is:%s\n", buf);
			std::cout << msg;
		}
		buffer_read.add(ItemCache(std::string(buf,buflen),fd));
	}
	return true;
}

bool epoll_socket::do_write(int fd)
{
	ItemCache it;
	if(!buffer_write.pop(it))
	{
		return false;
	}
	if(it.fd!=fd){
		snprintf(err_msg, sizeof(err_msg)
			, "write error,fd(%d,%d)\n",it.fd,fd);
		std::cout << err_msg;
	}
	int nwrite;
	nwrite = write(fd, it.data.c_str(), it.data.length());
	if (nwrite == -1)
	{
		snprintf(err_msg, sizeof(err_msg)
			, "write error: %s(errno: %d)\n"
			, strerror(errno), errno);
		std::cout << err_msg;
		del_client(fd);
		close(fd);
		del_event(fd, EPOLLOUT);
		return false;
	}
	else{ 
		if(print_flag){
			char msg[MAXBUFFSIZE] = { 0 };
			sprintf(msg, "write message is:%s\n", it.data.c_str());
			std::cout << msg;
		}
		mod_event(fd, EPOLLIN);
	}
	return true;
}

char * epoll_socket::get_errmsg()
{
	return err_msg;
}

void epoll_socket::setPrintFlag(bool flag)
{
	print_flag = flag;
}

void epoll_socket::setClientBlock(bool flag)
{
	isblock_client = flag;
}
